﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Data.Entity;
using System.Linq.Expressions;

namespace DataAccess.Repository
{
    /// <summary>
    /// 数据访问基类，封装了Insert、Update、Delete、FindEntity、FindList等基本方法
    /// </summary>
    /// <typeparam name="TDbContext">数据操作上下文</typeparam>
    /// <typeparam name="TEntity">数据对象</typeparam>
    public partial class RepositoryFactory<TDbContext, TEntity> : IRepositoryBase<TEntity>
        where TDbContext : DbContext, new()
        where TEntity : class, new()
    {
        #region 构造对象

        protected DbContext dbContext;

        public RepositoryFactory()
        {
            dbContext = new TDbContext();
        }

        #endregion

        #region 写数据

        /// <summary>
        /// 插入一条数据
        /// </summary>
        /// <param name="entity">表实体对象</param>
        /// <returns></returns>
        public int Insert(TEntity entity)
        {
            dbContext.Entry<TEntity>(entity).State = System.Data.Entity.EntityState.Added;
            return dbContext.SaveChanges();
        }

        /// <summary>
        /// 插入一批数据
        /// </summary>
        /// <param name="entities">表数据列表对象</param>
        /// <returns></returns>
        public int Insert(List<TEntity> entities)
        {
            foreach (var entity in entities)
            {
                dbContext.Entry<TEntity>(entity).State = System.Data.Entity.EntityState.Added;
            }
            return dbContext.SaveChanges();
        }

        /// <summary>
        /// 更新一个实体（所有字段必须赋值，不然会赋默认值）
        /// </summary>
        /// <param name="entity">实体对象</param>
        /// <returns></returns>
        public int Update(TEntity entity)
        {
            if (null == entity) return 0;
            #region 旧算法
            //dbContext.Set<TEntity>().Attach(entity);
            #endregion
            PropertyInfo[] props = entity.GetType().GetProperties();
            #region 新算法
            var idFiled = props.FirstOrDefault(x => x.Name == "Id");
            if (null == idFiled) return 0;
            var idValue = idFiled.GetValue(entity, null);
            var model = dbContext.Set<TEntity>().Find(idValue);
            if (null == model) return 0;
            #endregion
            foreach (PropertyInfo prop in props)
            {
                if (prop.GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.Schema.NotMappedAttribute), false).Any()) { continue; }
                #region 旧算法
                //if (prop.GetValue(entity, null) != null)
                //{
                //    var value = prop.GetValue(entity, null).ToString();
                //    if (value.Trim() == "" || value == "&nbsp;")
                //    {
                //        dbContext.Entry(entity).Property(prop.Name).CurrentValue = null;
                //    }
                //    dbContext.Entry(entity).Property(prop.Name).IsModified = true;
                //}
                //else
                //{
                //    dbContext.Entry(entity).Property(prop.Name).CurrentValue = null;
                //    dbContext.Entry(entity).Property(prop.Name).IsModified = true;
                //}
                #endregion
                #region 新算法
                var updateValue = prop.GetValue(entity, null);
                var dataBaseValue = prop.GetValue(model, null);
                if (dataBaseValue == updateValue) continue;
                prop.SetValue(model, updateValue);
                #endregion
            }
            return dbContext.SaveChanges();
        }

        /// <summary>
        /// 更新一批实体（所有字段必须赋值，不然会赋默认值）
        /// </summary>
        /// <param name="entities">表数据列表对象</param>
        /// <returns></returns>
        public int Update(List<TEntity> entities)
        {
            foreach (var entity in entities)
            {
                if (null == entity) continue;
                #region 旧算法
                //dbContext.Set<TEntity>().Attach(entity);
                #endregion
                PropertyInfo[] props = entity.GetType().GetProperties();
                #region 新算法
                var idFiled = props.FirstOrDefault(x => x.Name == "Id");
                if (null == idFiled) continue;
                var idValue = idFiled.GetValue(entity, null);
                var model = dbContext.Set<TEntity>().Find(idValue);
                if (null == model) continue;
                #endregion
                foreach (PropertyInfo prop in props)
                {
                    if (prop.GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.Schema.NotMappedAttribute), false).Any()) { continue; }
                    #region 旧算法
                    //if (prop.GetValue(entity, null) != null)
                    //{
                    //    var value = prop.GetValue(entity, null).ToString();
                    //    if (value.Trim() == "" || value == "&nbsp;")
                    //    {
                    //        dbContext.Entry(entity).Property(prop.Name).CurrentValue = null;
                    //    }
                    //    dbContext.Entry(entity).Property(prop.Name).IsModified = true;
                    //}
                    #endregion
                    #region 新算法
                    var updateValue = prop.GetValue(entity, null);
                    var dataBaseValue = prop.GetValue(model, null);
                    if (dataBaseValue == updateValue) continue;
                    prop.SetValue(model, updateValue);
                    #endregion
                }
            }

            return dbContext.SaveChanges();
        }

        /// <summary>
        /// 删除一个对象
        /// </summary>
        /// <param name="entity">对象实体</param>
        /// <returns></returns>
        public int Delete(TEntity entity)
        {
            dbContext.Set<TEntity>().Attach(entity);
            dbContext.Entry<TEntity>(entity).State = System.Data.Entity.EntityState.Deleted;
            return dbContext.SaveChanges();
        }

        /// <summary>
        /// 根据条件删除对象
        /// </summary>
        /// <param name="predicate">条件</param>
        /// <returns></returns>
        public int Delete(Expression<Func<TEntity, bool>> predicate)
        {
            var entitys = dbContext.Set<TEntity>().Where(predicate).ToList();
            entitys.ForEach(m => dbContext.Entry<TEntity>(m).State = System.Data.Entity.EntityState.Deleted);
            return dbContext.SaveChanges();
        }

        #endregion

        #region 读数据

        /// <summary>
        /// 根据主键查找一个实体
        /// </summary>
        /// <param name="id">主键</param>
        /// <returns></returns>
        public TEntity FindEntity(object id)
        {
            var entity = dbContext.Set<TEntity>().Find(id);
            //if (null != entity) dbContext.Entry(entity).State = EntityState.Detached;
            return entity;
        }

        /// <summary>
        /// 根据条件查找一个实体
        /// </summary>
        /// <param name="predicate">条件</param>
        /// <returns></returns>
        public TEntity FindEntity(Expression<Func<TEntity, bool>> predicate)
        {
            var entity = dbContext.Set<TEntity>().FirstOrDefault(predicate);
            //if (null != entity) dbContext.Entry(entity).State = EntityState.Detached;
            return entity;
        }

        /// <summary>
        /// 对数据类型已知的特定数据源的查询进行计算的功能
        /// </summary>
        /// <returns></returns>
        public IQueryable<TEntity> IQueryable()
        {
            return dbContext.Set<TEntity>();
        }

        /// <summary>
        /// 对数据类型已知的特定数据源的查询进行计算的功能
        /// </summary>
        /// <param name="predicate">条件</param>
        /// <returns></returns>
        public IQueryable<TEntity> IQueryable(Expression<Func<TEntity, bool>> predicate)
        {
            return dbContext.Set<TEntity>().Where(predicate);
        }

        /// <summary>
        /// 根据sql查询列表
        /// </summary>
        /// <param name="sql">sql</param>
        /// <returns></returns>
        public List<TEntity> FindList(string sql)
        {
            return dbContext.Database.SqlQuery<TEntity>(sql).ToList<TEntity>();
        }

        public List<TEntity> FindList(Expression<Func<TEntity, bool>> predicate)
        {
            return dbContext.Set<TEntity>().Where(predicate).ToList();
        }

        public int Count(Expression<Func<TEntity, bool>> predicate)
        {
            return dbContext.Set<TEntity>().Count(predicate);
        }

        public int Count()
        {
            return dbContext.Set<TEntity>().Count();
        }

        #endregion
    }
}
